#include "client.h"

#include <openssl/blowfish.h>
#include "eventQueue.h"
#include <sstream>
#include <iostream>
#include "listener.h"
using namespace std;

client::client()
{
	link_id=0;
}

client::~client()
{

}

void
client::startClientThread(const char *hostname, int portno)
{
	pthread_create(new pthread_t, NULL,&client::runClient, new accessHelper(this,(void*)0,hostname,portno));
}

void
*client::runClient(void* args)
{

	accessHelper* ah = static_cast<accessHelper*>(args);
	client* This = ah->This;
	int portno = ah->portno;

	This->establishServerConnection(ah->hostname.c_str(),portno);

	pthread_exit(NULL);

}

void
client::establishServerConnection(const char *hostname, int portno)
{
	//std::cout<<"A_"<<std::endl;
	static int bufferSize=64;
	int n;
	struct sockaddr_in serv_addr;
	struct hostent *server;

	com_params cp;

	// Create several sockets which will then be used for chat and file transfer

	cp.sockChat = socket(AF_INET, SOCK_STREAM, 0);
	cp.sockFile_protocol = socket(AF_INET, SOCK_STREAM, 0);
	cp.sockFile_send = socket(AF_INET, SOCK_STREAM, 0);
	cp.sockFile_receive = socket(AF_INET, SOCK_STREAM, 0);

	//std::cout<<"A"<<std::endl;

	server = gethostbyname(hostname);

	if (server == NULL) {
		// event for no such host message
	}
	else
	{

		//std::cout<<"B"<<std::endl;

		bzero((char *) &serv_addr, sizeof(serv_addr));
		serv_addr.sin_family = AF_INET;

		bcopy((char *)server->h_addr,
			  (char *)&serv_addr.sin_addr.s_addr,
			  server->h_length);

		serv_addr.sin_port = htons(portno);

		stringstream ss;
		eventStore es;

		// connect chat sock
		if (connect(cp.sockChat,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
		{
			// event error establishing connection
		}else
		{
			// now send my nick
			comms::sendNick(charNick,cp);

			char namebuf[bufferSize];
			n = recv(cp.sockChat,namebuf,bufferSize,0); // obtain connected name

			namebuf[n]='\0';
			std::cout<<"namebuf client: "<<namebuf<<" and n: "<<n<<std::endl;

			//std string str("Connection established to peer ");
			cp.peerNick=std::string(namebuf);

			// event connection established here!

			// obtain ip address...can add this to connection list etc.
			char str[INET_ADDRSTRLEN];
			inet_ntop(AF_INET, &(serv_addr.sin_addr), str, INET_ADDRSTRLEN);

			//std::cout<<"D"<<std::endl;

			stringstream ss("");
			ss << "Connection established to peer (server) " << string(str) << " with username "<<string(namebuf)<<std::endl;

			//eventStore es;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=7; // 7 so that peer is displayed
			es.extra=std::string(namebuf);
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			ss.str("");
			ss << "Generating RSA key pair.."<<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			comms::encryptor->generateRSAKeyPair(2048);

			ss.str("");
			ss << "RSA key pair generated.."<<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			// transmit public key which will be used to encrypt
			// symmetric key generated by peer
			comms::encryptor->transmitPublicKey(cp.sockChat,2048);

			ss.str("");
			ss << "RSA public key transmitted.."<<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			// now we receive symmetric key that was encrypted with our public key
			comms::encryptor->receiveEncryptedSymmetricKey(cp.sockChat);

			ss.str("");
			ss << "Received encrypted symmetric key and decrypted.."<<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

		}

		//tid_chat=pthread_create(new pthread_t, NULL,&MyFrame::chatReceive, new accessHelper(this,(void*)1) );

		// connect file sock
		bool gooda=false,goodb=false,goodc=false;
		if (connect(cp.sockFile_protocol,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
		{
			ss.str("");
			ss << "Error in creating file transfer protocol channel.."<<std::endl;

			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);
		}else
		{
			gooda=true;
		}

		// note have to connect sockFile_* in opposite order to which server
		// is listening in
		if (connect(cp.sockFile_receive,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
		{
			ss.str("");
			ss << "Error in creating file receive channel.."<<std::endl;

			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);
		}else
		{
			goodb=true;
		}

		if (connect(cp.sockFile_send,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
		{
			ss.str("");
			ss << "Error in creating file send channel.."<<std::endl;

			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);
		}else
		{
			goodc=true;
		}
		if(gooda&&goodb&&goodc)
		{
			ss.str("");
			ss << "File transfer channels established.."<<std::endl;

			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			ss.str("");
			ss << "Connected!"<<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);

			comms::all_com_params.push_back(cp);

			cm->startThreads(link_id);
			link_id++;
		}
	}



	//startListenThreads();
}
